package com.du.config;

import com.du.aop.LogAspects;
import com.du.aop.MathCalculator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * @author djg
 * @date 2020/11/26
 * AOP：【动态代理】
 * 指在程序运行期间动态的将某段代码切入到指定位置进行运行的编程方式
 * <p>
 * 1、导入aop模块，Spring AOP(Spring-aspects)
 * 2、定义一个业务逻辑类MathCalculator，在业务逻辑运行的时候将日志进行打印（方法之前，方法运行之后，方法出现异常）
 * 3、定义个日志切面类（LogAspects），切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行
 *      通知方法：
 *          前置通知（@Before）：logStart，在目标方法div运行之前运行
 *          后置通知（@After）：logEnd，在目标方法div运行结束之后运行(无论方法是正常还是异常结束)
 *          返回通知（@AfterReturning）：logReturn，在目标方法div正常返回之后运行
 *          异常通知（@AfterThrowing）：logException，在目标方法div出现异常以后运行
 *          环绕通知(@Around)：动态代理，手动推进目标方法运行（joinPoint.proceed（））
 * 4、给切面类的目标方法标注何时何地运行（通知注解）
 * 5、将切面类和业务逻辑类（目标方法所在类）都加入到容器中
 * 6、必须告诉Spring哪个类是切面类（给切面类加上一个注解@Aspect）
 * 7、给配置类中加@EnableAspectJAutoProxy，启用AspectJ自动代理【开启基于注解的Aop模式】
 *      在Spring中有很多的@EnableXXX
 *
 *
 * AOP三步：
 * 1、将业务逻辑组件和切面类都加入到容器中，告诉Spring哪个是切面类（@Aspect）
 * 2、在切面类上的每一个通知方法上标注通知注解，告诉Spring何时何地运行（切入点表达式）
 * 3、开启基于注解的AOP模式@EnableAspectJAutoProxy
 *
 * AOP的原理：【看给容器中注册了什么组件，这个组件什么时候工作，这个组件的功能是什么？其他的@EnableXXX同样的原理】
 *      @EnableAspectJAutoProxy
 * 1、@EnableAspectJAutoProxy是什么？
 *      @Import(AspectJAutoProxyRegistrar.class) 给容器中导入了AspectJAutoProxyRegistrar
 *          利用AspectJAutoProxyRegistrar自定义给容器中注册Bean； BeanDefinition
 *          internalAutoProxyCreator = AnnotationAwareAspectJAutoProxyCreator
 *      给容器中注册一个AnnotationAwareAspectJAutoProxyCreator（注解方式的自动代理创建器）
 *
 * 2、AnnotationAwareAspectJAutoProxyCreator
 *      继承关系：
 *      AnnotationAwareAspectJAutoProxyCreator
 *          ->AspectJAwareAdvisorAutoProxyCreator
 *              ->AbstractAdvisorAutoProxyCreator
 *                  ->AbstractAutoProxyCreator
 *                      implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
 *                  关注后置处理器（BeanPostProcessor），在bean初始化完成前后做的事情，自动注入BeanFactory
 * 断点调试的位置：
 *      AbstractAutoProxyCreator.setBeanFactory()
 *      AbstractAutoProxyCreator.postProcessBeforeInstantiation()有后置处理器的逻辑
 *
 *      AbstractAdvisorAutoProxyCreator.setBeanFactory()->initBeanFactory()
 *
 *      AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
 *
 * 断点调试结果：
 * 1、传入配置类，调用有参构造器创建IOC容器
 * 2、注册配置类，调用refresh()刷新容器
 * 3、registerBeanPostProcessors(beanFactory); 注册bean的后置处理器来方便拦截bean的创建
 *      3.1 先获取IOC容器中已经定义了的需要创建对象的所有BeanPostProcessor；String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
 *      3.2 给容器中加别的BeanPostProcessor；beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
 *      3.3 优先注册实现了PriorityOrdered接口的BeanPostProcessor
 *      3.4 再给容器中注册实现了Ordered接口的BeanPostProcessor
 *      3.5 注册没实现优先级接口的BeanPostProcessor
 *      3.6 注册BeanPostProcessor，实际上就是创建BeanPostProcessor对象保存在容器中
 *          创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
 *              3.6.1 创建Bean的实例 instanceWrapper = createBeanInstance(beanName, mbd, args);
 *              3.6.2 populateBean(beanName, mbd, instanceWrapper); 给Bean的属性赋值
 *              3.6.3 exposedObject = initializeBean(beanName, exposedObject, mbd); 初始化Bean
 *                  Bean初始化流程：
 *                  1、invokeAwareMethods(beanName, bean); 处理Aware接口的方法回调
 *                  2、applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); 应用后置处理器的postProcessABeforeInitialization()
 *                  3、invokeInitMethods(beanName, wrappedBean, mbd); 执行自定义的初始化方法
 *                  4、applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); 执行后置处理器的postProcessAfterInitialization()
 *              3.6.4、BeanPostProcessor（AnnotationAwareAspectJAutoProxyCreator）创建成功；-->aspectJAdvisorsBuilder
 *       3.7 把BeanPostProcessor注册到BeanFactory中
 *              beanFactory.addBeanPostProcessor(postProcessor)
 * ======================以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程=============================
 * AnnotationAwareAspectJAutoProxyCreator使用的InstantiationAwareBeanPostProcessor类型的后置处理器
 * 4、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作；创建剩下的单实例Bean
 *      4.1 遍历获取容器中的所有Bean，依次创建对象getBean(beanName)
 *          getBean()->doGetBean()->getSingleton()->
 *      4.2 创建Bean
 *          【AnnotationAwareAspectJAutoProxyCreator在所有Bean创建之前会有一个拦截，因为它是这个后置处理器InstantiationAwareBeanPostProcessor，会调用postProcessBeforeInstantiation()】
 *          4.2.1 先从缓存中获取当前Bean，如果能获取到，说明Bean是之前被创建过的，就直接使用。否则再创建；（只要创建好的Bean，都会被缓存起来）
 *          4.4.2 createBean(beanName, mbd, args); 创建Bean；
 *           AnnotationAwareAspectJAutoProxyCreator会在任何bean创建之前先尝试返回Bean的实例
 *          【BeanPostProcessor实在Bean对象创建完成初始化前后调用的】
 *          【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试使用后置处理器返回对象的】
 *              1、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation，希望后置处理器在此能返回一个代理对象，如果能返回代理对象就使用，如果不能就继续下一步
 *                  1、后置处理器先尝试返回对象
 *                  bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
 *                      拿到所有的后置处理器，如果是InstantiationAwareBeanPostProcessor，就执行postProcessBeforeInstantiation()
 *                  if (bean != null) {
 * 						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
 *                  }
 *              2、doCreateBean(beanName, mbdToUse, args); 真正的创建一个Bean实例，和【3.6流程】一样
 *
 * AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用：
 * 1、每一个Bean创建之前，调用postProcessBeforeInstantiation()
 *      关心MathCalculator和LogAspect的创建
 *      1.1 判断当前Bean是否在advisedBeans中（保存了所有需要增强的bean）
 *      1.2 判断当前Bean是否是基础类型的Advice,Pointcut,Advisor,AopInfrastructureBean,或者是否是切面（aspectJAdvisorFactory.isAspect(hasAspectAnnotation()),也就是是否使用@Aspect标注）
 *      1.3 是否需要跳过shouldSkip()
 *          1.3.1 获取候选的增强器（切面里面的通知方法）【List<Advisor> candidateAdvisors】；
 *                每一个封装通知方法的增强器是InstantiationModelAwarePointcutAdvisor类型的；
 *                判断每一个增强器是否是AspectJPointcutAdvisor类型的，如果是返回true
 *          1.3.2 shouldSkip() 永远返回false
 * 2、创建MathCalculator对象
 * postProcessAfterInitialization
 *      return wrapIfNecessary(bean, beanName, cacheKey); //包装，如果需要的情况下
 *      getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
 *      2.1、获取当前Bean的所有增强器（通知方法） Object[] specificInterceptors
 *         2.1.1 找到候选的所有增强器（找哪些通知方法是需要切入当前bean方法的）List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
 *         2.1.2 获取能在当前bean使用的增强器
 *         2.1.3 给增强器排序 eligibleAdvisors = sortAdvisors(eligibleAdvisors);
 *      2.2 保存当前bean在advisedBeans中
 *      2.3 如果当前bean需要增强，创建当前bean的代理对象 createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource)
 *          2.3.1 获取所有的增强器（通知方法）
 *          2.3.2 保存到proxyFactory
 *          2.3.3 创建代理对象，Spring自动决定使用哪种方式的动态代理
 *              JdkDynamicAopProxy(config);jdk动态代理
 *              ObjenesisCglibAopProxy(config); cglib动态代理
 *      2.4 给容器中返回当前组件使用cglib增强了的代理对象
 *      2.5 以后容器中获取到的就是这个组件的代理对象，执行目标方法的时候，代理对象就会执行通知方法的流程
 * 3、目标方法执行
 *      容器中保存了组件的代理对象（cglib增强后的对象），这个对象里面保存了详细信息（比如增强器，目标对象，xxx）
 *      3.1 CglibAopProxy.intercept() 拦截目标方法的执行
 *      3.2 根据ProxyFactory对象获取将要执行的目标方法的拦截器链
 *          List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 *          3.2.1 List<Object> interceptorList 保存所有的拦截器，并且赋值长度为config.getAdvisors().length=5
 *              （config.getAdvisors()包含了ExposeInvocationInterceptor和四个拦截器）
 *          3.2.2 遍历所有的增强器，将其转为Interceptor，registry.getInterceptors(advisor);
 *          3.2.3 将增强器转为List<MethodInterceptor>
 *                如果是MethodInterceptor，直接加入到集合中
 *                如果不是，使用AdvisorAdapter将增强器转为MethodInterceptor
 *                转化完成，返回MethodInterceptor数组
 *      3.3 如果没有拦截器链，直接执行目标方法
 *          拦截器链（每一个通知方法又被包装成方法拦截器，利用MethodInterceptor机制）
 *      3.4 如果有拦截器链，把需要直接的目标对象，目标方法，拦截器链等信息传入创建一个CglibMethodInvocation对象，并调用它的.proceed()方法，返回Object retVal
 *      3.5 拦截器链的触发过程
 *          3.5.1 如果没有拦截器执行目标方法，或者拦截器的索引和拦截器数组-1大小一样（执行到最后一个拦截器），然后执行目标方法
 *              if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1)
 *          3.5.2 链式获取每一个拦截器，拦截器执行invoke方法，每一个拦截器等待下一个拦截器执行完成返回以后再来执行
 *                拦截器链的机制，保证通知方法与目标方法的执行顺序
 *
 * 总结：
 *      1、@EnableAspectJAutoProxy 开启AOP功能
 *      2、@EnableAspectJAutoProxy 会给容器中注册一个组件AnnotationAwareAspectJAutoProxyCreator
 *      3、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器
 *      4、容器的创建流程：
 *          4.1 registerBeanPostProcessors(beanFactory); 注册后置处理器，创建AnnotationAwareAspectJAutoProxyCreator对象
 *          4.2 finishBeanFactoryInitialization(beanFactory); 初始化剩下的单实例bean
 *              4.2.1 创建业务逻辑组件和切面组件
 *              4.2.2 AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
 *              4.2.3 组件创建完成之后，判断组件是否需要增强 postProcessAfterInitialization
 *                    是：把切面通知方法包装成增强器（Advisor），给业务逻辑组件创建一个代理对象（cglib）
 *      5、执行目标方法
 *          5.1 代理对象执行目标方法
 *          5.2 CglibAopProxy.intercept()
 *              5.2.1 得到目标方法的拦截器链（增强器包装成拦截器MethodInterceptor）
 *              5.2.2 利用拦截器的链式机制，依次进入每一个拦截器进行执行
 *              5.2.3 效果：
 *                    正常执行：前置通知->目标方法->后置通知->返回通知
 *                    出现异常：前置通知->目标方法->后置通知->异常通知
 *
 *
 *
 *
 *
 *
 */
@Configuration
@EnableAspectJAutoProxy
public class MainConfigOfAOP {

    //业务逻辑类加入到容器中
    @Bean
    public MathCalculator mathCalculator() {
        return new MathCalculator();
    }

    //切面类加入到容器中
    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }
}




















